package pearson.automation.api;

import java.io.OutputStreamWriter;
import java.net.Authenticator;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import framework.BaseEntity;

/**
 * Base API Call class.
 */
class BaseAPICall extends BaseEntity {

    /**
     * Perform a basic API call.
     * @param apiURLTemplate
     *              Template URL
     * @param parameters
     *              POST parameters for api call
     * @param checkCallStatus
     *              Check 'status' tag == 'success'
     * @return XML with response
     */
    @SuppressWarnings("rawtypes")
    public final Document performAPICall(
            final String apiURLTemplate,
            final HashMap<String, String> parameters,
            final boolean checkStatus) {
         /* debug(String.format("API call %1$s, params $2%s",
                apiURLTemplate, parameters.toString()));*/

        //Cut auth info from URL
        String apiURL = String.format(
                "%1$s%2$s", browser.getApiURL(), apiURLTemplate);
        Document doc = null;
        try {
            // Parse parameters
            String data = "";

            if (parameters != null) {
                Iterator i = parameters.entrySet().iterator();
                while (i.hasNext()) {
                    Map.Entry me = (Map.Entry) i.next();
                    data +=
                        String.format("%s=%s&",
                          URLEncoder.encode(me.getKey().toString(), "UTF-8"),
                          URLEncoder.encode(me.getValue().toString(), "UTF-8"));
                }
            }

            // Send data
            Authenticator.setDefault(new MyAuthenticator());
            URL url = new URL(apiURL);
            URLConnection conn = url.openConnection();

            OutputStreamWriter wr = null;
            if (!data.isEmpty()) {
                conn.setDoOutput(true);
                wr = new OutputStreamWriter(conn.getOutputStream());
                wr.write(data);
                wr.flush();
            }

            // Get the response
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            doc = db.parse(new InputSource(conn.getInputStream()));
            doc.getDocumentElement().normalize();
            if (!data.isEmpty()) {
                wr.close();
            }
            checkAPICallResult(doc, checkStatus);
        } catch (Exception e) {
            warn(e.toString());
        }
        return doc;
    }

    /**
     * Check that API call was successful.
     * @param document
     *          XML document with API response
     * @param checkCallStatus
     *              Check 'status' tag == 'success'
     */
    protected void checkAPICallResult(
            final Document document,
            final boolean checkStatus) {
        try {
            NodeList nodeList = document.getElementsByTagName("status");
            for (int iIndex = 0; iIndex < nodeList.getLength(); iIndex++) {
                if (nodeList.item(iIndex).getNodeType() == Node.ELEMENT_NODE) {
                     Element element = (Element) nodeList.item(iIndex);
                     if ((!element.getTextContent().equals("success"))
                      && (checkStatus == true)){
                         fatal("Call failed, result is not success");
                    } else { 
                        return;
                    }
                 }
            }
        } catch (Exception e) {
            warn(String.format("Exception occured: %s", e.toString()));
        }
    }

    /**
     * Get results from API response.
     * @param document
     *          XML response
     * @param rootTagName
     *          Root tag name with results
     * @param searchedTagName
     *          Tag name with required values
     * @return An array of searchedTagName contents
     */
    protected ArrayList<String> getAPICallResults(
            final Document document,
            final String rootTagName,
            final String searchedTagName) {
        ArrayList<String> returnData = new ArrayList<String>();
        try {
            NodeList nodeList = document.getElementsByTagName(rootTagName);
            for (int iIndex = 0; iIndex < nodeList.getLength(); iIndex++) {
                if (nodeList.item(iIndex).getNodeType() == Node.ELEMENT_NODE) {
                     Element element = (Element) nodeList.item(iIndex);
                     returnData.add(element.getAttribute(searchedTagName));
                 }
            }
        } catch (Exception e) {
            fatal(String.format("Exception occured: %s", e.toString()));
        }
        return returnData;
    }

    @Override
    protected String formatLogMsg(final String message) {
        return String.format("API: %s", message);
    }
}
